/*
 *  hlfs/src/clean/su_check.c
 *
 *  Harry Wei <harryxiyou@gmail.com> 2011 (C)
 */

#include <time.h>
#include <glib.h>
#include <sys/time.h>
#include <stdlib.h>

#include "hlfs.h"
#include "clean.h"
#include "misc.h"
#include "storage.h"
#include "address.h"

/*
 * get_last_inode_addr: get the segment's lastest log's inode address.
 * @para ctrl: the global control structure of HLFS.
 * @para segfile: the segment file name.
 * @return: if error happens return -1, if right return the inode address.
 */
uint64_t get_last_inode_addr(struct hlfs_ctrl *ctrl, char *segfile) 
{
#if 0
	bs_file_info_t *info = NULL;

	if (NULL == (info = ctrl->storage->bs_file_info(ctrl->storage, segfile))) {
		g_message("%s -- get segfile info error!\n", __func__);
		return -1;
	}
	return info->size - INODEM_LEN - INODE_LEN;
#endif
#if 1
	bs_file_t file = NULL;
	bs_file_info_t *info = NULL;
	struct inode_map_entry im;
	
	if (0 == ctrl->storage->bs_file_is_exist(ctrl->storage, segfile)) {
		if (NULL == (file = ctrl->storage->bs_file_open(ctrl->storage, segfile, BS_READONLY))) {
			g_message("%s -- open segfile error!\n", __func__);
			return -1;
		}
		if (NULL == (info = ctrl->storage->bs_file_info(ctrl->storage, segfile))) {
			ctrl->storage->bs_file_close(ctrl->storage, file);
			g_message("%s -- get segfile info error!\n", __func__);
			return -1;
		}
		g_message("%s -- segfile size:------------>%lld\n", __func__, info->size);
		if (INODEM_LEN != ctrl->storage->bs_file_pread(ctrl->storage, file, 
							&im, INODEM_LEN, info->size - INODEM_LEN)) {
			ctrl->storage->bs_file_close(ctrl->storage, file);
			g_message("%s -- pread error!\n", __func__);
			return -1;
		}
	} else {
		g_message("%s -- segfile not exist!\n", __func__);
		return -1;
	}
	ctrl->storage->bs_file_close(ctrl->storage, file);
	g_message("%s -- im.inode_addr:------------>%lld\n", __func__, (im.inode_addr & SEGMENT_SIZE_MASK));
	return im.inode_addr & SEGMENT_SIZE_MASK;
#endif
}

/*
 * load_inode: load the inode into RAM.
 * @para ctrl: the global control of hlfs.
 * @para segfile: the segment file name.
 * @para inode_addr: the inode address.
 * @return: if error happens return NULL, if right return inode's RAM address.
 */
struct inode *load_inode(struct hlfs_ctrl *ctrl, char *segfile, uint64_t inode_addr)
{
	bs_file_t file = NULL;
	struct inode *inode = NULL;
	
	inode = g_malloc0(sizeof(struct inode));
	if (!inode) {
		g_message("%s -- Allocate Error!\n", __func__);
		return NULL;
	}
	g_message("%s -- inode_addr------------------> %lld\n", __func__, inode_addr);
	if (0 == ctrl->storage->bs_file_is_exist(ctrl->storage, segfile)) {
		if (NULL == (file = ctrl->storage->bs_file_open(ctrl->storage, segfile, BS_READONLY))) {
			g_free(inode);
			g_message("%s -- open segfile error!\n", __func__);
			return NULL;
		}
		if (INODE_LEN != ctrl->storage->bs_file_pread(ctrl->storage, file, inode, INODE_LEN, inode_addr)) {
			g_free(inode);
			ctrl->storage->bs_file_close(ctrl->storage, file);
			g_message("%s -- pread error!\n", __func__);
			return NULL;
		}
	} else {
		g_message("%s -- segfile not exist!\n", __func__);
		return NULL;
	}
	return inode;
}

/*
 * get_db_addr: get the particular datablock number's address.
 * @para ctrl: the global control structure of hlfs.
 * @para inode: the inode structure.
 * @para db_no: the particular data block number.
 * @return: right return particular db's address, error return -1.
 */
uint64_t get_db_addr(struct hlfs_ctrl *ctrl, struct inode *inode, uint64_t db_no) 
{
	uint64_t cur_storage_addr = 0;
	guint32 BLOCKSIZE = ctrl->sb.block_size;

	if (is_db_in_level1_index_range(db_no)) {
		int idx = db_no % 12;
		cur_storage_addr = inode->blocks[idx];
	} else if (is_db_in_level2_index_range(db_no)) {
		if (0 == ctrl->inode.iblock) {
			return 1;
		}
		uint64_t *ib = (uint64_t *)read_block(ctrl->storage, inode->iblock, BLOCKSIZE);
		if (NULL == ib) {
			return -1;
		}
		int idx = (db_no - 12) % 1024;
		cur_storage_addr = *(ib + idx);
		g_free(ib);
	} else if (is_db_in_level3_index_range(db_no)) {
		if(0 == ctrl->inode.doubly_iblock) {
			return 1;
		}
		uint64_t *ib = (uint64_t *)read_block(ctrl->storage, ctrl->inode.doubly_iblock, BLOCKSIZE);
		if (NULL == ib) {
			return -1;
		}
		int idx = (db_no - 12 - 1024) / 1024;
		if (0 == *(ib + idx)) {
			return 1;
		}
		uint64_t *ib2 = (uint64_t *)read_block(ctrl->storage, *(ib + idx), BLOCKSIZE);
		if (NULL == ib2) {
			return -1;
		}
		uint64_t idx2 = (db_no - 12 - 1024) % 1024;
		cur_storage_addr = *(ib2 + idx2);
		g_free(ib);
		g_free(ib2);
	} else if (is_db_in_level4_index_range(db_no)) {
		if(0 == ctrl->inode.triply_iblock) {
			return 1;
		}
		uint64_t *ib = (uint64_t *)read_block(ctrl->storage, ctrl->inode.triply_iblock, BLOCKSIZE);
		if (NULL == ib) {
			return -1;
		}
		int idx = (db_no - 12 - 1024 - 1024 * 1024) / (1024 * 1024);
		if (0 == *(ib + idx)) {
			return 1;
		}
		uint64_t *ib2 = (uint64_t *)read_block(ctrl->storage, *(ib + idx), BLOCKSIZE);
		if (NULL == ib2) {
			return -1;
		}
		uint64_t idx2 = (db_no - 12 - 1024 - 1024 * 1024) / 1024 % 1024;
		if (NULL == *(ib2 + idx2)) {
			return 1;
		}
		uint64_t *ib3 = (uint64_t *)read_block(ctrl->storage, *(ib2 + idx2), BLOCKSIZE);
		if (NULL == ib3) {
			return -1;
		}
		uint64_t idx3 = (db_no - 12 - 1024 - 1024 * 1024) % 1024;
		cur_storage_addr = *(ib3 + idx3);
		g_free(ib);
		g_free(ib2);
		g_free(ib3);
	} else {
		g_message("%s -- index error!\n", __func__);
		return -1;
	}
	return cur_storage_addr;
}
/*
 * load_segment_usage: load the segment usage into segment usage table.
 * @para ctrl: the global control for hlfs.
 * @return: void.
 */
void load_segment_usage(struct hlfs_ctrl *ctrl)
{
	int i = 0;
	int j = 0;
	int tmp = 0;
	uint64_t pos = 0;
	bs_file_t file = NULL;
	struct segment_usage *temp[SEG_NUM];
	struct segment_usage *su = NULL;
	ctrl->su_table = g_array_new(FALSE, FALSE, sizeof(struct segment_usage));

	if (0 == ctrl->storage->bs_file_is_exist(ctrl->storage, "segment_usage")) {
		//open the segment_usage file and init the GArray
		g_message("%s --------------------------------> pass 1\n", __func__);
		bs_file_info_t *f_st = ctrl->storage->bs_file_info(ctrl->storage, "segment_usage");
		if (NULL == (file = ctrl->storage->bs_file_open(ctrl->storage, "segment_usage", BS_READONLY))) {
			g_message("%s - bs_file_open error!\n", __func__);
			g_free(temp);
			g_free(su);
			exit(EXIT_FAILURE);
		}
		while (pos < f_st->size) {
			temp[j] = (struct segment_usage *)g_malloc0(sizeof(struct segment_usage));
			if (!temp[j]) {
				g_message("%s - Allocate Error!\n", __func__);
				exit(EXIT_FAILURE);
			}
			if (SU_LEN != ctrl->storage->bs_file_pread(ctrl->storage, file, temp[j], SU_LEN, pos)) {
				g_message("%s - bs_file_pread error!\n", __func__);
				g_free(temp);
				g_free(su);
				exit(EXIT_FAILURE);
			}
			g_array_append_vals(ctrl->su_table, temp[j], 1);
			for (i = 0; i < ctrl->su_table->len; i++) { /* find the newest entry of su*/
				su = &g_array_index(ctrl->su_table, struct segment_usage, i);
				if (temp[j]->segno == su->segno) {
					g_array_remove_index(ctrl->su_table, i);
					g_array_append_vals(ctrl->su_table, temp[j], 1);
				}
			}
			pos += SU_LEN;
			j += 1;
		}
#if 0
	for (i = 0; i < ctrl->su_table->len; i++) {
		su = &g_array_index(ctrl->su_table, struct segment_usage, i);
		g_message("segno: ---------------->%lld\n", su->segno);
		sleep(2);
		g_message("alive_blocks: ---------------->%lld\n", su->alive_blocks);
		sleep(2);
		for (tmp = 0; tmp < MAP_NUM; tmp++) {
			g_message("bitmap: ---------------->%lld\n", su->alive_log_bitmap[tmp]);
		}
		g_message("timestamp: ---------------->%lld\n", su->timestamp);
		sleep(2);
	}
#endif
		ctrl->storage->bs_file_close(ctrl->storage, file);
	} else {
		g_message("%s --------------------------------> pass 2\n", __func__);
		for (i = 0; i <= ctrl->last_segno; i++) {
			temp[i] = (struct segment_usage *)g_malloc0(sizeof(struct segment_usage));
			if (!temp[i]) {
				g_message("%s - Allocate Error!\n", __func__);
				exit(EXIT_FAILURE);
			}
			temp[i]->segno = i;
			temp[i]->alive_blocks = 0;
			for (tmp = 0; tmp < MAP_NUM; tmp++)
				temp[i]->alive_log_bitmap[tmp] &= BIT_SHIFT;
			temp[i]->timestamp = 0;
			g_array_append_vals(ctrl->su_table, temp[i], 1);
		}
	}
#if 0
	for (i = 0; i < ctrl->su_table->len; i++) {
		su[i] = (struct segment_usage *)g_malloc0(sizeof(struct segment_usage));
		if (!su[i]) {
			g_message("%s - Allocate Error!\n", __func__);
			g_free(temp);
			exit(EXIT_FAILURE);
		} 
		su[i] = &g_array_index(ctrl->su_table, struct segment_usage, i);
		g_message("segno: ---------------->%lld\n", su[i]->segno);
		g_message("alive_blocks: ---------------->%lld\n", su[i]->alive_blocks);
		for (tmp = 0; tmp < MAP_NUM; tmp++) {
			g_message("bitmap: ---------------->%lld\n", su[i]->alive_log_bitmap[tmp]);
		}
		g_message("timestamp: ---------------->%lld\n", su[i]->timestamp);
	}
#endif
	return ;
}

/*
 * add_new: add the new entry for su table.
 * @para ctrl: global control of hlfs.
 * @para su_table: su table of su.
 * @return: an integer, which 0 is right.
 */
int add_new(struct hlfs_ctrl *ctrl)
{
	uint64_t i = 0;
	uint64_t j = 0;
	uint64_t tmp = 0;
	uint8_t flag = 0;
	char *seg_name = NULL;
	struct segment_usage *tem = NULL;
	struct segment_usage *su = NULL;

	tem = (struct segment_usage *)g_malloc0(sizeof(struct segment_usage));
	if (!tem) {
		g_message("%s - Allocate Error!\n", __func__);
		exit(EXIT_FAILURE);
	}
	su = (struct segment_usage *)g_malloc0(sizeof(struct segment_usage));
	if (!su) {
		g_message("%s - Allocate Error!\n", __func__);
		g_free(tem);
		exit(EXIT_FAILURE);
	}
	for (i = 0; i < ctrl->last_segno; i++) {
		seg_name = build_segfile_name(i);
		if (0 == ctrl->storage->bs_file_is_exist(ctrl->storage, seg_name)) {
			for (tmp = 0; tmp < ctrl->su_table->len; tmp++) {
				tem = &g_array_index(ctrl->su_table, struct segment_usage, tmp);
				if (tem->segno == i) { /* XXX if we should load the newest one */
					flag = 1;
					break;
				}
			}
		}
		if (0 == flag) {
			su->segno = i;
			su->alive_blocks = -1;
			for (j = 0; j < MAP_NUM; j++)
				su->alive_log_bitmap[j] |= BIT_SHIFT;
			su->timestamp = 0; /* XXX BUG Here */
			g_array_append_val(ctrl->su_table, su);
		} 
		flag = 0;
	}
	g_message("%s ----------------------------> pass 1\n");
//	g_free(tem);
//	g_free(su);
	return 0;
}
#if 0

/*
 * get_stirage_address: achive the block address.
 * @para ctrl: the global control of hlfs.
 * @para inode: hlfs's inode structure.
 * @para bno: block number.
 * @return: bno's address.
 */
uint64_t get_storage_address(struct hlfs_ctrl *ctrl, struct inode *inode, uint64_t bno)
{
	return inode->blocks[0] + bno * (ctrl->sb).block_size;
}

#endif
/*
 * dump_segment_usage_table: dump the segment_usage file.
 * @para ctrl: the global control of hlfs.
 * @return: void.
 */
void dump_segment_usage_table(struct hlfs_ctrl *ctrl)
{
	uint64_t i = 0;
	bs_file_t file = NULL;
	struct segment_usage *su = NULL;

	su = (struct segment_usage *)g_malloc0(sizeof(struct segment_usage));
	if (!su) {
		g_message("%s - Allocate Error!\n", __func__);
		exit(EXIT_FAILURE);
	}
	if (0 == ctrl->storage->bs_file_is_exist(ctrl->storage, "segment_usage")) {
		if (NULL ==  (file = ctrl->storage->bs_file_open(ctrl->storage, "segment_usage", BS_WRITEABLE))) {
			g_message("%s - open segment_usage file error!\n", __func__);
			g_free(su);
			exit(EXIT_FAILURE);
		}
		for (i = 0; i < ctrl->su_table->len; i++) {
			su = &g_array_index(ctrl->su_table, struct segment_usage, i);
			if (SU_LEN != ctrl->storage->bs_file_append(ctrl->storage, file, su, (uint32_t) SU_LEN)) {
				g_message("%s - bs_file_append error\n", __func__);
				g_free(su);
				exit(EXIT_FAILURE);
			}
		}
	} else {
		if (NULL == (file = ctrl->storage->bs_file_create(ctrl->storage, "segment_usage"))) {
			g_message("%s - create segment_usage file error!\n", __func__);
			g_free(su);
			exit(EXIT_FAILURE);
		}
		for (i = 0; i < ctrl->su_table->len; i++) {
			su = &g_array_index(ctrl->su_table, struct segment_usage, i);
			if (SU_LEN != ctrl->storage->bs_file_append(ctrl->storage, file, su, (uint32_t) SU_LEN)) {
				g_message("%s - bs_file_append error\n", __func__);
				g_free(su);
				exit(EXIT_FAILURE);
			}
		}
	}
	ctrl->storage->bs_file_close(ctrl->storage, file);
	return ;
}

/*
 * su_check_task: calculate the alive blocks in a segment file.
 * @para ctrl: the global controal of hlfs.
 * @para status: the status of su_check_task.
 * @return: the segment usage table.
 */
void su_check_task(struct hlfs_ctrl *ctrl)
{
	uint32_t counter = 1;
	uint32_t i;
	uint32_t tmp;
//	uint8_t status = RUN;
	char *std_seg_name = NULL;
	char *seg_name = NULL;
	uint32_t interval = 1;
	uint64_t idx = 0; 
	uint64_t log_idx = 0;
	uint64_t log_inarray_idx = 0;
	uint64_t seg_size = 0;
	uint32_t offset = 0;
	uint64_t cur_block_addr = 0;
	uint64_t block_addr = 0;
	struct inode per_inode;
	struct log_header lh;
	struct inode *std_inode = NULL;
	struct segment_usage *su = NULL;
#if 0
	su = (struct segment_usage *)g_malloc0(sizeof(struct segment_usage));
	if (!su) {
		g_message("%s - Allocate Error!\n", __func__);
		exit(EXIT_FAILURE);
	}
#endif
	std_inode = (struct inode *)g_malloc0(sizeof(struct inode));
	if (!std_inode) {
		g_message("%s - Allocate Error!\n", __func__);
		exit(EXIT_FAILURE);
	}
	/* load the segment file to segment usage table */
	g_message("pass here --------------------------------------->1\n");
	load_segment_usage(ctrl);
	for (i = 0; i < ctrl->su_table->len; i++) {
		su = &g_array_index(ctrl->su_table, struct segment_usage, i);
		g_message("segno: ---------------->%lld\n", su->segno);
		g_message("alive_blocks: ---------------->%lld\n", su->alive_blocks);
		for (tmp = 0; tmp < MAP_NUM; tmp++) {
			g_message("bitmap: ---------------->%lld\n", su->alive_log_bitmap[tmp]);
		}
		g_message("timestamp: ---------------->%lld\n", su->timestamp);
	}
#if 1
	g_message("pass here --------------------------------------->2\n");
	while (counter) {
#if 0
		if (status == STOP) 
			break;
		if (status == SUSPEND) {
			g_message("%s -- sleep here!\n", __func__);
			sleep(interval);
			continue;
		}
#endif
#if 0		
		if (0 == idx) /* when idx == 0, add the new entry into segment usage table*/
			add_new(ctrl);
#endif
		g_message("%s -- pass here --------------------------------------->3\n", __func__);
		g_message("%s -- std_seg no: ------------------->%lld\n", __func__, ctrl->last_segno - 1);
		std_seg_name = build_segfile_name(ctrl->last_segno - 1);
		std_inode = load_inode(ctrl, std_seg_name, get_last_inode_addr(ctrl, std_seg_name));
		g_message("%s -- pass here --------------------------------------->4\n", __func__);
		su = &g_array_index(ctrl->su_table, struct segment_usage, idx);
		seg_name = build_segfile_name(su->segno);
		bs_file_info_t *info = NULL;
		if (NULL == (info = ctrl->storage->bs_file_info(ctrl->storage, seg_name))) {
			g_message("%s -- get segfile info error!\n", __func__);
			return -1;
		}
		if (ctrl->storage->bs_file_is_exist(ctrl->storage, seg_name)) {
				g_message("%s - no this segment file!\n", __func__);
				g_array_remove_index(ctrl->su_table, su->segno);
				idx += 1;
				continue;
		}
		bs_file_t file = ctrl->storage->bs_file_open(ctrl->storage, seg_name, BS_READONLY);
		if (!file) {
			g_message("%s - bs_file_open error!\n", __func__);
			exit(EXIT_FAILURE);
		}
		seg_size = 0;
		offset = 0;
		log_idx = 0;
		log_inarray_idx = 0;
		while (seg_size < info->size) { 
			if (LOG_HEADER_LENGTH != ctrl->storage->bs_file_pread(ctrl->storage, file, &lh, LOG_HEADER_LENGTH, offset)) {
				g_message("%s - bs_file_pread log_header error!\n", __func__);
				exit(EXIT_FAILURE);
			}
			g_message("-------------------------->here>????\n");
			log_idx += 1;
			offset += lh.log_size;
			printf("db_num: lh.db_num %ld\n", lh.db_num);
			if (0 == lh.db_num) {
				seg_size += lh.log_size;
				continue;

			}
			if (sizeof(struct inode) != ctrl->storage->bs_file_pread(ctrl->storage, file, 
						&per_inode, INODE_LEN, offset - INODE_LEN - INODEM_LEN)) {
				g_message("%s - bs_file_pread inode error!\n", __func__);
				exit(EXIT_FAILURE);

			}
			for (i = lh.start_db_no, tmp = 0; i < lh.db_num + (uint32_t)lh.start_db_no - 1; i++) {
				g_message("2 -------------------------->here>????\n");
				g_message("start: %lld\n", lh.start_db_no);
				g_message("end: %ld\n", lh.db_num + lh.start_db_no - 1);
				cur_block_addr = get_db_addr(ctrl, std_inode, i); 
				block_addr = get_db_addr(ctrl, &per_inode, i);
				if (cur_block_addr == block_addr) {
					su->alive_blocks += 1;
					su->alive_log_bitmap[tmp] |= BIT_CHECK << log_inarray_idx;
				}
			}
			g_message("1 -------------------------->here>????\n");
			if (0 == (log_idx % 64)) {
				log_inarray_idx = 0;
				tmp += 1;
			}
			printf("seg_size---------------->%lld\n", seg_size);
			seg_size += lh.log_size;
			log_inarray_idx += 1;	/* we use alive_log_bitmap[MAP_NUM] to represent alive logs */
		}
		idx += 1;
		if (idx >= ctrl->su_table->len) {
			idx = 0;
//			status = SUSPEND;
			counter = 0;
			dump_segment_usage_table(ctrl); 
		}
		ctrl->storage->bs_file_close(ctrl->storage, file);
		g_message("%s -- over --------------------------->!!!!\n", __func__);
	}
#endif 
	return ;
}
